﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SceneLib;

namespace OpenGL
{
    public class Boid
    {
        private static Random random = new Random();

        private static float space = 2.0f;
        private static float fseparation = 0.5f;        
        private static float fflock = 0.01f;
        private static float ffalign = 0.125f;
        private static float h = 0.1f;
        
        public Vector position;
        public Vector velocity;
        private Vector center;
        private float radius;

        public Boid(Vector center, float radius)
        {
            this.center = center.Copy();
            this.radius = radius;
            position = new Vector(center.x + radius * Convert.ToSingle(random.NextDouble() - 0.5),
                                center.y + radius * Convert.ToSingle(random.NextDouble() - 0.5),
                                center.z + radius * Convert.ToSingle(random.NextDouble() - 0.5));
            velocity = new Vector(Convert.ToSingle(random.NextDouble()),
                Convert.ToSingle(random.NextDouble()),
                Convert.ToSingle(random.NextDouble()));
        }

        public void Move(List<Boid> boids)
        {
            Vector force = fseparation * Repulsive(boids) +
                        fflock * Flock(boids) +
                        ffalign * Alignment(boids) + fseparation * Bound();

            velocity = velocity + h * force;

            velocity.Normalize3();

            position = position + h * velocity;
        }

        private Vector Flock(List<Boid> boids)
        {
            Vector xcenter = new Vector();

            foreach (Boid boid in boids)
            {
                if (boid != this)
                {
                    xcenter += boid.position;
                }
            }

            xcenter = (1.0f / (boids.Count - 1)) * xcenter;

            return xcenter - position;
        }

        private Vector Repulsive(List<Boid> boids)
        {
            Vector resp = new Vector();

            foreach (Boid boid in boids)
            {
                if (boid != this)
                {
                    float distance = (boid.position - position).Magnitude3();

                    if (distance < space)
                    {
                        resp = resp - (boid.position - position);
                    }                    
                }
            }

            return resp;
        }

        private Vector Alignment(List<Boid> boids)
        {
            Vector vcenter = new Vector();

            foreach (Boid boid in boids)
            {
                if (boid != this)
                {
                    vcenter += boid.velocity;
                }
            }

            vcenter = (1.0f / (boids.Count - 1)) * vcenter;

            return vcenter - velocity;
        }

        private Vector Bound()
        {
            Vector resp = new Vector();

            if (position.x < center.x - radius)
                resp += new Vector(Math.Abs(position.x - center.x + radius), 0.0f, 0.0f);
            if (position.x > center.x + radius)
                resp += new Vector(-Math.Abs(position.x - center.x - radius), 0.0f, 0.0f);

            if (position.y < center.y - radius * 0.8f)
                resp += new Vector(0.0f, Math.Abs(position.y - center.y + radius * 0.8f), 0.0f);
            if (position.y > center.y + radius)
                resp += new Vector(0.0f, -Math.Abs(position.y - center.y - radius), 0.0f);

            if (position.z < center.z - radius)
                resp += new Vector(0.0f, 0.0f, Math.Abs(position.z - center.z + radius));
            if (position.z > center.z + radius)
                resp += new Vector(0.0f, 0.0f, -Math.Abs(position.z - center.z - radius));

            return resp;
        }
    }
}
